
import java.io.IOException;
import java.util.HashMap;
import java.util.Hashtable;

import javax.rmi.CORBA.Util;

public class Client {
	/**
	 * Class client.
	 * 
	 */

	int id;		// id unique du client
	int id_conn; // id de connexion pour differents services
	int etat;	// Etat du client (0=inactif, 1=contention, 2=actif)
	int serv_class; // classe de service : 1=best effort , 2=Real Time
	int activ_behav; // definit le taux d'activité du client 
	int location; // distance des clients par rapport a la station de b (de 0 à 10)
	int cdma; // code CDMA
	boolean connection_succes=false; // true si le client a reussi le protocole de connection
	int b; // parametre b dans l'algo beb 
	int w; // parametre beb
	int w0=16; // doit toujours etre pair !
	int wmax=1024;
	int transmission_limit; // limite des transmissions de demande d'acces 
	long nb_data_to_transfert; // nombre de donnees a transmettre 
	

	String empreinte; // empreinte d ecriture sur rsc

	long data_send; // nb de data envoyees
	long data_received; // nb de data recues
	public long total_data_received; // total
	public long total_data_send;
	boolean init_connection_done=false;
	boolean transmited = false;
	boolean collision;
	int cpt=0; // compteur
	boolean controle_collision;
	boolean transmission_fail; 
	int nb_collision;
	int nb_coll_detect_after_transmit=5; 
	// nb de trames pendant lequel le client va attendre avant de detecter la collision
	// seuil de detection de collision en nombre de trames ne contenant pas l'id dans la map dl et ul 
	int nb_transmit=0;
	boolean temps_attente_defini = false; // init flag
	int attente = 0;	// temps d'attente avant le prochain passage en contention. (beb)
	
//===========================================================================
	public Client(int id,int id_conn,int etat, int serv_class, int activ_behav, int loc, int cdma,int nb_to_transfert,int activ_beh){

		this.id=id;
		this.id_conn=id_conn;
		this.etat=etat;
		this.serv_class=serv_class;
		this.activ_behav=activ_behav;
		this.location=loc;
		this.cdma=cdma;
		this.nb_data_to_transfert=nb_to_transfert;
		this.activ_behav=activ_beh;
		this.transmission_limit=Utils.client_trasmission_limit;
		
		if(this.etat==2) // on met le client en contention 
			this.etat=1;

		this.init_beb();
	}
//===========================================================================
	protected void read_data(Trame t1, int s){ // les clients lisent leurs donnees uniquement au slot 1

		if(s==1){ // debut de la trame 

			this.read_dl_map(t1);
			this.read_ul_map(t1);
			
			if(this.serv_class==1){
				
				if (Utils.log==1)
					try {Utils.writeF("Client "+this.id_conn +" Classe BE"+" Reading data... "+ "DATA received=" + this.data_received+" Connection succed ="+this.connection_succes+" Transmited = "+this.transmited , "console");} 
					catch (IOException e) {e.printStackTrace();}
					////System.out.println("Client "+this.id_conn +" Classe BE"+" Reading data... "+ "DATA received=" + this.data_received+" Connection succed ="+this.connection_succes+" Transmited = "+this.transmited);
					//System.out.println("Client "+this.id_conn + " reste a telecharger : "+ (this.nb_data_to_transfert - this.data_received));
			}
			
			if(this.serv_class==2){
				
				if (Utils.log==1)
					try {Utils.writeF("Client "+this.id_conn +" Classe RT"+" Reading data... "+ "DATA received=" + this.data_received+" Connection succed ="+this.connection_succes+" Transmited = "+this.transmited , "console");} 
					catch (IOException e) {e.printStackTrace();}
					////System.out.println("Client "+this.id_conn +" Classe RT"+" Reading data... "+ "DATA received=" + this.data_received+" Connection succed ="+this.connection_succes+" Transmited = "+this.transmited);
				
			}
			this.check_transfert_fini(t1); // check si le transfert est fini 
		}
	}
//===========================================================================
	protected void read_ul_map(Trame t1){	

		// on compte les datas qui nous concernent  dans la trame Ul_Map
		//System.out.println("Client "+this.id_conn+" reading UL map ");

		if(t1.get_UL_map().containsKey(this.id_conn)){
			this.data_send = (this.data_send + (t1.get_UL_map().get(this.id_conn)));
		}
		//else System.out.println("rien pour moi dans UL map..");
	}
//===========================================================================
	protected void read_dl_map(Trame t1){

		//	System.out.println("Client "+this.id_conn+" reading DL map ");

		if(t1.get_DL_map().containsKey(this.id_conn)){
			this.data_received = (this.data_received + (t1.get_DL_map().get(this.id_conn)));
			
			if (Utils.log==1)
			try {Utils.writeF("feed"+ t1.get_DL_map().get(this.id_conn) , "console");} 
			catch (IOException e) {e.printStackTrace();}
			////System.out.println("feed"+ t1.get_DL_map().get(this.id_conn) );
		}
	}
//===========================================================================
	protected void init_connection(Trame t1, int s,int t){ // etape de contention (BEB)

		if(this.transmited==false)
			this.algo_beb(t1, s,t);
	}
//===========================================================================
	private void act_behavior(int t){// taux d'activite des client 

		if (this.temps_attente_defini == false) { // On execute le temps d'attente qu'une suele fois
			
			this.attente = t + Utils.random(0, this.activ_behav);
			this.temps_attente_defini = true;
		}
		
		if(t == this.attente) {	// vérifie si c'est l'heure de se réveiller
			this.etat=1;
			if (Utils.log==1)
			try {Utils.writeF( "Client "+this.id_conn+" is waking up !", "console");} 
			catch (IOException e) {e.printStackTrace();}
			////System.out.println("Client "+this.id_conn+" is waking up !");
			this.temps_attente_defini = false; // reset flag
			this.init_beb();
			
			if (Utils.log==1)
			try {Utils.writeF("Client "+ this.id_conn +" Init New BEB" , "console");} 
			catch (IOException e) {e.printStackTrace();}
			////System.out.println("Client "+ this.id_conn +" Init New BEB");
		}
	}
//===========================================================================
	protected void comportement(Trame t1, int s, int t){ // definit le comportement des clients 

		if(this.etat==0){ // client inactif 
			if (Utils.log==1)
			try {Utils.writeF("ID cli "+this.id_conn+"  inactif" , "console");} 
			catch (IOException e) {e.printStackTrace();}
			////System.out.println("ID cli "+this.id_conn+"  inactif");
			this.act_behavior(t);
			return;
		}

		if(this.transmited && s==1){ // si le client a transmit sa demande et que on est au slot1

			this.is_collision(t1); // on regarde dans les map si il y'a des donnees ou pas 
		}

		if(this.collision==false && this.transmited==true && this.connection_succes==true){ // test si le client est deja connecte
			this.read_data(t1, s);
			return;
		}
		else { this.init_connection(t1, s, t); 
		}
	}
//===========================================================================
	protected boolean listen_canal(){ // ecoute le canal, renvoi false si occupe , true si libre

		if(Utils.slot_emission.containsValue(this.cdma))
			return false;
		else return true;
	}
//===========================================================================
	protected void is_collision(Trame t1){ // si la partie FCH de la trame contient le cdma de ce client alors collision il y'a eu

		if(t1.get_DL_map().containsKey(this.id_conn)){ // le client est dans DL_MAP donc connecte !
			this.connection_succes=true;
			this.etat=2; // etat actif 
			this.collision=false;
			this.nb_transmit=0; // on remet le nombre de transmission de l'acces au canal a 0;
		}

		else{
			this.cpt++;

			if(this.cpt==this.nb_coll_detect_after_transmit){
				this.collision=true;	// il y'a eu collision
				this.transmited=false; // la transmission de la demande a échouer
				this.connection_succes = false; // Ne pas oublier de reseter cette variable...
				this.nb_collision++; // + 1 dans le nb de collision 
				Utils.total_collisions++;
				
				if (Utils.log==1)
					try {Utils.writeF(this.id_conn+" - Collision!!" , "console");} 
					catch (IOException e) {e.printStackTrace();}
				////System.out.println(this.id_conn+" - Collision!!");
				this.init_beb_collision(); // on initialise le BEB en mode collision 
				this.cpt=0;  // si le client est plusieurs fois en collision, on remet le compteur a 0
			}
		}
	}
//===========================================================================
	protected void transmit(Trame tr, int s){ // transmission dans le ranging sub channel (uplink)
		// s est le slot de temps en cours 

		if(this.nb_transmit<=this.transmission_limit){

			this.empreinte = Utils.make_empreinte(this.id_conn, this.serv_class, this.location);
			tr.write_RangingSubCh(s,this.empreinte);

			Utils.emissions_client.put(this.id_conn, this.cdma); // pour les collisions 
			Utils.slot_emission.put(this.id_conn,this.cdma);// pour listen canal 

			if (Utils.log==1)
				try {Utils.writeF("Client "+this.id_conn+ " CDMA "+ this.cdma+" Transmit ! Write empreinte--> "+this.empreinte , "console");} catch (IOException e) {e.printStackTrace();}
				////System.out.println("Client "+this.id_conn+ " CDMA "+ this.cdma+" Transmit ! Write empreinte--> "+this.empreinte);

			this.transmited=true;
			this.nb_transmit++;
			Utils.nb_demande_acces++;
		}
		else {
			if (Utils.log==1)
				try {Utils.writeF("Limite de transmission atteinte !" , "console");} 
				catch (IOException e) {e.printStackTrace();}
				////System.out.println("Limite de transmission atteinte !");
		}
	}
//===========================================================================	
	private void check_transfert_fini(Trame t1){ // verifie si il y a encore des data a dl
		
		if(this.nb_data_to_transfert<=this.data_received){
			
			t1.ul_transfert_fini.add(this.id_conn); // inscrit la demande de deconnexion dans ul burst 
			
			if (Utils.log==1)
			try {Utils.writeF("id client "+this.id_conn+" Transfert data termine !!!"+" -Mode veille " , "console");} 
			catch (IOException e) {e.printStackTrace();}
			//System.out.println("id client "+this.id_conn+" Transfert data termine !!!"+" -Mode veille ");
			this.etat=0;// etat veille 
			this.transmited = false;// ******* ???
			this.nb_data_to_transfert = Utils.random(10, 50); // ????
			this.total_data_received += this.data_received; ////////: ???
			this.data_received = 0;/// ???????
			this.connection_succes = false; //??????
		}
	}
//===========================================================================
	private void init_beb(){

		this.w=this.w0;		 //init de w 
		this.b=Utils.random(0, this.w-1); // init de b

		//System.out.println("Client "+this.id_conn+" init_BEB" );
	}
//===========================================================================
	private void init_beb_collision(){ // initialise le beb apres collision 

		if(this.w>=this.wmax){ // w a atteint wmax ??
			this.b=Utils.random(0, this.w-1);
		}
		else {
			this.w=this.w*2; // doubler la taille de la fenetre de contention 
			this.b=Utils.random(0, this.w-1);
		}
		if (Utils.log==1)
			try {Utils.writeF("Client "+this.id_conn+" init_BEB_collision" , "console");} 
		catch (IOException e) {e.printStackTrace();}
		////System.out.println("Client "+this.id_conn+" init_BEB_collision" );
	}
//===========================================================================
	public void algo_beb(Trame tr, int s, int t){
		// on controle si on est bien dans la range de UL frame afin de ne pas faire de BEB dans la DL frame
		if(s>Utils.pos_curseur){
			
			if (Utils.log==1)
				try {Utils.writeF("Client "+this.id_conn+" algo BEB B=" + this.b + " CDMA "+ this.cdma , "console");} 
				catch (IOException e) {e.printStackTrace();}
				//System.out.println("Client "+this.id_conn+" algo BEB B=" + this.b + " CDMA "+ this.cdma);

			if(this.b==0) {
				this.transmit(tr,s); // transmission
				this.collision=false ; // test 
			}
			else {
				if(this.listen_canal()==false){ // ecoute le canal

					if (Utils.log==1)
						try {Utils.writeF("client "+this.id_conn+" CDMA "+ this.cdma+" canal occupe" , "console");} 
						catch (IOException e) {e.printStackTrace();}
						//System.out.println("client "+this.id_conn+" CDMA "+ this.cdma+" canal occupe" );
				}
				else {this.b--;}
			}
		}
	}
//===========================================================================

}// end class

